#pragma once

#include <algorithm>
#include <boost/foreach.hpp>

#include "storage_policies.hpp"
#include "access_policies.hpp"
#include "predicates.hpp"




namespace boostext
{
	/**
	 * A sequence is merely a wrapper around any sequence, like for example
	 * a vector, a list or a c-style array.
	 *
	 * The storage_policy defines how the sequence will be stored
	 * (for example in the actual container, or merely as iterators),
	 * as well as if the sequence is sorted or ordered randomly.
	 *
	 * An example would be a vector of numbers, sorted ascending.
	 * One can then create a sequence view and fill it with a certain range, like for example
	 * [0, 10[. The given view will in return only provide iterators to all numbers within that range.
	 */
	template
	<
		typename storage_policy
	>
	class sequence
		: public storage_policy
	{
	public:

		typedef storage_policy   SP;
		typedef sequence<SP>     _This;

		typedef typename SP::iterator         iterator;
		typedef typename SP::const_iterator   const_iterator;

		typedef typename SP::value_type                       value_type;
		typedef typename boost::add_const<value_type>::type   const_value_type;


		template
		<
			typename filter_policy                            = no_filter,
			template <typename, typename> class access_policy = unlimited_access,
			typename view_storage_policy                      = iterator_storage< typename SP::iterator, typename SP::const_iterator >
		>
		class view
			: public view_storage_policy
			, public access_policy< SP, filter_policy >
		{
		public:

			/// Typedef for the storage policy of this view
			typedef view_storage_policy                  VSP;

			/// Typedef for the access policy
			typedef access_policy< SP, filter_policy >   AP;

			typedef typename AP::iterator                iterator;
			typedef typename AP::const_iterator          const_iterator;

		private:

			//const _This&   m_resource;

		public:

			/**
			 * Create a view from the given sequence.
			 */
			view(_This& that)
				// The storage policy is given access to the entire sequence
				// it may copy it or do something complete different
				: VSP(that.begin(), that.end())
				// The access_policy on the other hand works on the interval, delivered by the storage policy
				, AP(VSP::storage_begin(), VSP::storage_end())
			{}

			/**
			 * Create a view from the given sequence.
			 */
			view(const filter_policy& pred, _This& that)
				// The storage policy is given access to the entire sequence
				// it may copy it or do something complete different
				: VSP(that.begin(), that.end())
				// The access_policy on the other hand works on the interval, delivered by the storage policy
				, AP(pred, VSP::storage_begin(), VSP::storage_end())
			{}

			/**
			 * Create a view from the given sequence.
			 */
			view(const _This& that)
				// The storage policy is given access to the entire sequence
				// it may copy it or do something complete different
				: VSP(that.begin(), that.end())
				// The access_policy on the other hand works on the interval, delivered by the storage policy
				, AP(VSP::storage_begin(), VSP::storage_end())
			{}

			/**
			 * Create a view from the given sequence.
			 */
			view(const filter_policy& pred, const _This& that)
				// The storage policy is given access to the entire sequence
				// it may copy it or do something complete different
				: VSP(that.begin(), that.end())
				// The access_policy on the other hand works on the interval, delivered by the storage policy
				, AP(pred, VSP::storage_begin(), VSP::storage_end())
			{}

			template <typename Iter>
			view(Iter begin, Iter end)
				: VSP(begin, end)
				, AP(VSP::storage_begin(), VSP::storage_end())
			{}

			template <typename Iter>
			view(const filter_policy& pred, Iter begin, Iter end)
				: VSP(begin, end)
				, AP(pred, VSP::storage_begin(), VSP::storage_end())
			{}



			/**
			 * TODO: Investigate why the compiler is unable to find this function
			 */
			template <typename Char, typename Traits>
			std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream) const
			{
				BOOST_FOREACH(const_value_type& value, *this)
					stream << value << " ";

				return stream;
			}
			///////////////////////////////////////////////////////////////////////////////////////////////
		};
		///////////////////////////////////////////////////////////////////////////////////////////

	private:

		bool m_changed;

	public:

		/**
		 * Create an empty sequence.
		 */
		sequence()
			: m_changed(false)
		{}

		/**
		 * Create a sequence from the given iterator range.
		 */
		template <typename Iter>
		sequence(Iter begin, Iter end)
			// The storage policy is given access to the entire sequence
			// it may copy it or do something complete different
			: SP(begin, end)
		{}
	};
	///////////////////////////////////////////////////////////////////////////////////////////////




	template
	<
		typename storage_policy
	>
	std::ostream& operator<<(std::ostream& stream, const sequence<storage_policy>& that)
	{
		BOOST_FOREACH(sequence<storage_policy>::const_value_type& value, that)
			stream << value << " ";

		return stream;
	}
	///////////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
